package cqrtplm.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hustcad.plm.basic.util.TyplmEntityBaseUtil;
import com.hustcad.plm.basicdataimport.dto.StdTypeDetailDTO;
import com.hustcad.plm.basicdataimport.mapper.StdTypeMapper;
import com.hustcad.plm.caddocapi.service.TyplmCADDocumentService;
import com.hustcad.plm.pdm.basedrule.dto.TyplmBaseRuleBoundedObjDTO;
import com.hustcad.plm.pdm.basedrule.service.TyplmBasedRuleService;
import com.hustcad.plm.pdm.code.model.dto.codemanagement.TyplmSegmentCodeConditionDTO;
import com.hustcad.plm.pdm.code.model.vo.codemanagement.segmentcode.TyplmSegmentCodeRuleVO;
import com.hustcad.plm.pdm.code.service.codemanagement.TyplmSegmentCodeService;
import com.hustcad.plm.pdm.doc.dto.CreateDocInputDTO;
import com.hustcad.plm.pdm.doc.dto.SaveDocDTO;
import com.hustcad.plm.pdm.doc.service.TyplmDocQueryService;
import com.hustcad.plm.pdm.doc.service.TyplmDocumentService;
import com.hustcad.plm.pdm.doc.vo.DocumentVO;
import com.hustcad.plm.pdm.file.model.dto.BatchUploadDTO;
import com.hustcad.plm.pdm.file.service.TyplmFileHandleService;
import com.hustcad.plm.pdm.filevault.model.vo.FileDataVO;
import com.hustcad.plm.pdm.folder.model.vo.SubfolderVO;
import com.hustcad.plm.pdm.folder.model.vo.SubfolderWebVO;
import com.hustcad.plm.pdm.folder.service.TyplmSubFolderLinkService;
import com.hustcad.plm.pdm.iba.service.TyplmAttributeValueService;
import com.hustcad.plm.pdm.lifestage.model.dto.EntityLifeCycleDTO;
import com.hustcad.plm.pdm.lifestage.service.TyplmLifecycleTemplateService;
import com.hustcad.plm.pdm.partbom.dto.ChildPartsFiltersDTO;
import com.hustcad.plm.pdm.partbom.dto.ExpandBomTreeByFilterDTO;
import com.hustcad.plm.pdm.partbom.dto.PartBomTree;
import com.hustcad.plm.pdm.partbom.service.TyplmPartBomService;
import com.hustcad.plm.pdm.partbom.service.TyplmPartDescribeLinkService;
import com.hustcad.plm.pdm.partbom.service.TyplmPartRelObjectService;
import com.hustcad.plm.pdm.partbom.vo.PartBomTreeByFilterVO;
import com.hustcad.plm.pdm.partbom.vo.PartDescribeDocVO;
import com.hustcad.plm.pdm.projectshared.service.project.TyplmProjectService;
import com.hustcad.plm.pdm.projectshared.vo.project.ProjectCommonVO;
import com.hustcad.plm.pdm.type.mapper.TyplmTypeMapper;
import com.hustcad.plm.pdm.type.model.vo.TypeVueVO;
import com.hustcad.plm.pdm.type.service.TyplmTypeService;
import com.lark.oapi.okhttp.OkHttpClient;
import com.lark.oapi.okhttp.Request;
import com.lark.oapi.okhttp.Response;
import com.ty.basic.constant.TableTypeConstrant;
import com.ty.basic.entity.IdentifierEntity;
import com.ty.basic.entity.cad.CADDocDO;
import com.ty.basic.entity.doc.DocDO;
import com.ty.basic.entity.lifecycle.LifeCycleTemplateDO;
import com.ty.basic.entity.part.PartDO;
import com.ty.basic.entity.type.TypeDO;
import com.ty.basic.service.snow.SnowflakeIdComponent;
import com.ty.basic.session.TyAccountContext;
import com.ty.basic.vo.AttributeVO;
import cqrtplm.common.CommonService;
import cqrtplm.constant.RTErrorCodeEnum;
import cqrtplm.dto.DrawingInfoDTO;
import cqrtplm.dto.OaUploadFileDTO;
import cqrtplm.dto.PartNumberDTO;
import cqrtplm.entity.ObjectStorageDO;
import cqrtplm.entity.RtOaFormMappingDO;
import cqrtplm.mapper.RTPartMapper;
import cqrtplm.mapper.RtOaFormMappingMapper;
import cqrtplm.mapper.StoragePlanningMapper;
import cqrtplm.service.DocFolderOperationService;
import cqrtplm.service.DocumentProcessor;
import cqrtplm.service.ObjectStorageService;
import cqrtplm.service.RtOaFormMappingService;
import cqrtplm.vo.PDFProjectVO;
import liquibase.pro.packaged.I;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class RtOaFormMappingServiceImpl implements RtOaFormMappingService {

    @Resource
    private RtOaFormMappingMapper rtOaFormMappingMapper;

    @Resource
    private TyplmDocumentService typlmDocumentService;

    @Resource
    private TyplmLifecycleTemplateService typlmLifecycleTemplateService;

    @Resource
    private SnowflakeIdComponent snowflakeIdComponent;

    @Resource
    private TyplmBasedRuleService typlmBasedRuleService;

    @Resource
    private TyplmSubFolderLinkService typlmSubFolderLinkService;

    @Resource
    private TyplmProjectService typlmProjectService;

    @Resource
    private StoragePlanningMapper storagePlanningMapper;

    @Resource
    private ObjectStorageService objectStorageService;

    @Resource
    private DocFolderOperationService docFolderOperationService;

    @Resource
    private TyplmSegmentCodeService typlmSegmentCodeService;

    @Resource
    private TyplmPartBomService typlmPartBomService;

    @Resource
    private TyplmCADDocumentService typlmCADDocumentService;

    @Resource
    private TyplmDocQueryService typlmDocQueryService;

    @Resource
    private TyplmAttributeValueService typlmAttributeValueService;

    @Resource
    private RTPartMapper rtPartMapper;

    @Resource
    private CommonService commonService;

    @Resource
    private TyplmPartRelObjectService typlmPartRelObjectService;

    @Resource
    private TyplmPartDescribeLinkService typlmPartDescribeLinkService;

    @Resource
    private TyplmFileHandleService typlmFileHandleService;

    @Resource
    private TyplmTypeMapper typlmTypeMapper;

    @Resource
    private StdTypeMapper stdTypeMapper;

    @Resource
    private TyplmTypeService typlmTypeService;

    @Value("${oa.changeInfo.url}")
    private String url;

    public static final String FILE_PATH = "/plm/drawing/";

    /**
     * 创建OA表单映射记录
     *
     * @param entity OA表单映射实体对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createRtOaFormMapping(RtOaFormMappingDO entity) {

        if (StrUtil.isNotBlank(entity.getDocType())) {

            Integer i = rtOaFormMappingMapper.selectOneByDocType(entity.getDocType());
            if (i > 0) {
                throw new RuntimeException("类型" + entity.getDocType() + "已存在");
            }
        } else {
            throw new RuntimeException("类型不能为null");
        }

        // 设置创建时间
        if (entity.getCreateTime() == null) {
            entity.setCreateTime(new Date());
        }
        // 设置修改时间
        if (entity.getModifyTime() == null) {
            entity.setModifyTime(new Date());
        }
        BigInteger oid = TyAccountContext.getUser().getOid();
        entity.setCreator(oid.toString());
        entity.setId(snowflakeIdComponent.getInstance().nextId());
        int result = rtOaFormMappingMapper.insert(entity);
        if (result > 0) {
            log.info("OA表单映射记录创建成功，ID：{}", entity.getId());
        } else {
            log.error("OA表单映射记录创建失败");
            throw new RuntimeException("OA表单映射记录创建失败");
        }
    }

    /**
     * 更新OA表单映射记录
     *
     * @param entity OA表单映射实体对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRtOaFormMapping(RtOaFormMappingDO entity) {
        // 设置修改时间
        entity.setModifyTime(new Date());

        int result = rtOaFormMappingMapper.update(entity);
        if (result > 0) {
            log.info("OA表单映射记录更新成功，ID：{}", entity.getId());
        } else {
            log.error("OA表单映射记录更新失败，ID：{}", entity.getId());
            throw new RuntimeException("OA表单映射记录更新失败");
        }
    }

    /**
     * 删除OA表单映射记录
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRtOaFormMapping(String type) {

        int result = rtOaFormMappingMapper.delete(type);
        if (result > 0) {
            log.info("OA表单映射记录删除成功，ID：{}", type);
        } else {
            log.error("OA表单映射记录删除失败，ID：{}", type);
            throw new RuntimeException("OA表单映射记录删除失败");
        }
    }

    /**
     * 根据ID获取OA表单映射记录
     *
     * @param id 主键ID
     * @return OA表单映射实体对象
     */
    @Override
    public RtOaFormMappingDO getRtOaFormMapping(BigInteger id) {
        return rtOaFormMappingMapper.selectById(id);
    }

    /**
     * 获取所有OA表单映射记录列表
     *
     * @return OA表单映射实体对象列表
     */
    @Override
    public List<RtOaFormMappingDO> list() {
        return rtOaFormMappingMapper.selectAll();
    }

    @Override
    public String SynchronousOA(JSONObject jsonObject) throws IOException {
        String jobOid = jsonObject.getString("jobOid");
        JSONObject json = jsonObject.getJSONArray("Deliverables").getJSONObject(0);
        String ruletypeDisplayName = json.getString("ruletypeDisplayName");
        String ruletypeName = json.getString("ruletypeName");
        JSONArray jsonArray = json.getJSONArray("deliverName");
        String ruleId = json.getString("oid");
        RtOaFormMappingDO rtOaFormMappingDO = rtOaFormMappingMapper.selectByType(ruletypeDisplayName);
        if (rtOaFormMappingDO == null) {
            throw new RuntimeException("同步OA失败类型未维护表单映射关系");
        }
        String oaFormInnerName = rtOaFormMappingDO.getOaFormInnerName();
        JSONObject js = buildOAFormData(oaFormInnerName);
        JSONObject filedJSONObject = new JSONObject();
        if (CollUtil.isEmpty(jsonArray)) {
            DocumentVO doc = createDoc(jobOid, ruleId, ruletypeDisplayName, ruletypeName);
            filedJSONObject.put("field0061", doc.getObjnumber());
            js.put("formmain_15442", filedJSONObject);
        } else {
            JSONObject docObject = jsonArray.getJSONObject(0);
            filedJSONObject.put("field0061", docObject.getString("Code"));
            js.put("formmain_15442", filedJSONObject);
        }

        String oaUrl = getOAUrl(js);
        return oaUrl;
    }


    @Override
    public void rotaChangeNotice(MultipartFile file, String workFlowContainerId) throws IOException {
        DocDO docDO = this.typlmDocQueryService.queryLastDocByNumber(workFlowContainerId);

        List<EntityLifeCycleDTO> list = new ArrayList<>();
        EntityLifeCycleDTO entityLifeCycleDTO = new EntityLifeCycleDTO();
        entityLifeCycleDTO.setOid(docDO.getOid());
        entityLifeCycleDTO.setOtype(TableTypeConstrant.TY_DOC_OTYPE);
        entityLifeCycleDTO.setLifeCycleStageKey("RELEASED");
        list.add(entityLifeCycleDTO);
        typlmLifecycleTemplateService.batchUpdateEntityLifecycle(list, false, false, false);

        BatchUploadDTO batchUploadDTO = new BatchUploadDTO();
        batchUploadDTO.setUpdatoroid(new BigInteger("105"));
        batchUploadDTO.setUpdatorotype(TableTypeConstrant.TY_USER_OTYPE);
        batchUploadDTO.setOid(docDO.getOid().toString());
        batchUploadDTO.setOtype(TableTypeConstrant.TY_DOC_OTYPE);
        batchUploadDTO.setIsMainFile(1);
        batchUploadDTO.setFileList(Collections.singletonList(file));
        typlmFileHandleService.batchUploadFile(batchUploadDTO);
    }


    public String getOAUrl(JSONObject jsonObj) {
        String str = jsonObj.toJSONString();
        OkHttpClient client = new OkHttpClient().newBuilder()
                .build();
        com.lark.oapi.okhttp.MediaType mediaType = com.lark.oapi.okhttp.MediaType.parse("application/json");
        com.lark.oapi.okhttp.RequestBody body = com.lark.oapi.okhttp.RequestBody.create(mediaType, str);
        Request request = new Request.Builder()
                .url(this.url)
                .method("POST", body)
                .addHeader("Content-Type", "application/json")
                .build();

        try {
            //            String str = URLEncoder.encode(data.toJSONString(), "UTF-8");
            Response response = client.newCall(request).execute();
            String json = response.body().string();

            if (StrUtil.isNotBlank(json)) {
                JSONObject jsonObject = JSONObject.parseObject(json);
                if (jsonObject.getString("success").equals("true")) {
                    return jsonObject.getString("url");
                }
            }
            return null;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private DocumentVO createDoc(String jobId, String ruleId, String typeName, String ruletypeName) throws IOException {

        SaveDocDTO saveDocDTO = new SaveDocDTO();
        ObjectStorageDO objectStorageDO = objectStorageService.selectOneByTypeDefName(typeName);
        CreateDocInputDTO createDocInputDTO = new CreateDocInputDTO();
        LifeCycleTemplateDO lifeCycleTemplateDO = typlmLifecycleTemplateService.queryLifecycleByName(
                "润通通用生命周期模板");
        TypeDO lastTypeByName = typlmTypeService.getLastTypeByName(ruletypeName);
        String typeId = lastTypeByName.getOid().toString();

        String projectId = rtOaFormMappingMapper.selectContainerIdByJobId(jobId);
        ProjectCommonVO projectCommonVO = typlmProjectService.queryProjectByOid(projectId);
        Optional<AttributeVO> encryptionlevel = null;
        if (projectCommonVO.getIbaAttribute() != null) {
            projectCommonVO.getIbaAttribute().stream().filter(
                    f -> f.getAttributeDefName().equals("encryptionlevel")).findAny();
        }

        TyplmBaseRuleBoundedObjDTO typeBasedRuleEditDTO = new TyplmBaseRuleBoundedObjDTO();

        if ((encryptionlevel != null && encryptionlevel.isPresent() && encryptionlevel.get().equals(
                "加密")) || Objects.isNull(objectStorageDO)) {
            String folderId = storagePlanningMapper.selectOidByNameAndCid(projectCommonVO.getOid().toString(),
                                                                          "同步OA文件", "0");
            if (StrUtil.isBlank(folderId)) {
                SubfolderVO subfolder = new SubfolderVO();
                subfolder.setName("同步OA文件");
                subfolder.setDescription("--系统自动创建--文件夹--");
                subfolder.setParentOid(projectCommonVO.getDefaultcabinetoid().toString());
                subfolder.setParentOtype("ty.inteplm.folder.CTyCabinet");
                SubfolderWebVO subfolderWebVO = typlmSubFolderLinkService.createSubfolder(subfolder);
                folderId = subfolderWebVO.getOid();

            }
            createDocInputDTO.setFolderId(folderId);
            createDocInputDTO.setFolderPath("/同步OA文件");
            createDocInputDTO.setContainerId(projectId);
            createDocInputDTO.setContainerType("ty.inteplm.project.CTyProject");
            typeBasedRuleEditDTO = typlmBasedRuleService.getBaseRuleBoundedObjInfo(new BigInteger(typeId),
                                                                                   new BigInteger(projectId),
                                                                                   "ty.inteplm.product.CTyProject");
        } else {
            PDFProjectVO v = docFolderOperationService.selectPdmIdsByName(objectStorageDO.getContextName());
            String folderId = null;
            String[] split = objectStorageDO.getFolderPath().split("/");
            for (int i = 0; i < split.length; i++) {
                String id = null;
                if (StrUtil.isBlank(split[i])) {
                    continue;
                }
                if (i == 1) {
                    id = storagePlanningMapper.selectOidByNameAndCid(v.getOId(),
                                                                     split[i], "0");
                    if (StrUtil.isNotBlank(id)) {
                        folderId = id;
                    }
                } else {
                    id = storagePlanningMapper.selectOidByNameAndCid(v.getOId(), split[i], folderId);
                    if (StrUtil.isNotBlank(id)) {
                        folderId = storagePlanningMapper.selectOidByNameAndCid(v.getOId(),
                                                                               split[i], folderId);
                    }

                }
                if (StrUtil.isBlank(id)) {
                    //创建文件夹
                    SubfolderVO subfolder = new SubfolderVO();
                    subfolder.setName(split[i]);
                    subfolder.setDescription("--系统自动创建--文件夹--");
                    subfolder.setParentOid(i == 1 ? v.getDefaultcabinetoid() : folderId);
                    subfolder.setParentOtype(
                            i == 1 ? "ty.inteplm.folder.CTyCabinet" : "ty.inteplm.folder.CTySubFolder");
                    SubfolderWebVO subfolderWebVO = typlmSubFolderLinkService.createSubfolder(subfolder);
                    folderId = subfolderWebVO.getOid();
                }
            }
            typeBasedRuleEditDTO = typlmBasedRuleService.getBaseRuleBoundedObjInfo(new BigInteger(typeId),
                                                                                   new BigInteger(v.getOId()),
                                                                                   "ty.inteplm.product.CTyPDMLinkProduct");
            createDocInputDTO.setFolderId(folderId);
            createDocInputDTO.setFolderPath(objectStorageDO.getFolderPath());
            createDocInputDTO.setContainerId(v.getOId());
            createDocInputDTO.setContainerType("ty.inteplm.product.CTyPDMLinkProduct");
        }

        IdentifierEntity ruleInfo = new IdentifierEntity(typeBasedRuleEditDTO.getSegmentCodeOid(),
                                                         typeBasedRuleEditDTO.getSegmentCodeOtype());
        TyplmSegmentCodeRuleVO segmentFormData = getSegmentFormData(ruleInfo);
        createDocInputDTO.setName("自动生成");//todo
        createDocInputDTO.setDocType(typeName);
        createDocInputDTO.setType(typeId);
        createDocInputDTO.setNumber(segmentFormData.getCode());
        createDocInputDTO.setLifecycleTemplateID(lifeCycleTemplateDO.getOid().toString());
        createDocInputDTO.setLifecycleTemplateName(lifeCycleTemplateDO.getName());
        saveDocDTO.setCreateDocInputDTO(createDocInputDTO);
        Map<String, List<String>> maps = new HashMap<>();
        saveDocDTO.setIbaAttribute(maps);
        DocumentVO documentVO = typlmDocumentService.insertDoc(saveDocDTO);
        try {

            OkHttpClient client = new OkHttpClient().newBuilder()
                    .connectTimeout(60, TimeUnit.SECONDS)
                    .readTimeout(60, TimeUnit.SECONDS)
                    .writeTimeout(60, TimeUnit.SECONDS)
                    .build();

            JSONObject jsonObject = buildOutRuleRemoteJSON(documentVO, ruleId, jobId, projectId);

            com.lark.oapi.okhttp.MediaType mediaType = com.lark.oapi.okhttp.MediaType.parse("application/json");
            com.lark.oapi.okhttp.RequestBody body = com.lark.oapi.okhttp.RequestBody.create(mediaType,
                                                                                            jsonObject.toJSONString());
            Request request = new Request.Builder()
                    .url("http://127.0.0.1:9588/api/v1/javapdmremote/insertRemoteDoc")
                    .method("PUT", body)
                    .addHeader("Content-Type", "application/json")
                    .addHeader("authorization", TyAccountContext.getToken())
                    .build();
            Response response = client.newCall(request).execute();
            String json = response.body().string();
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        return documentVO;
    }

    /**
     * 根据初始化规则获取码段规则
     *
     * @param ruleInfo
     * @return {@link TyplmSegmentCodeRuleVO }
     */
    private TyplmSegmentCodeRuleVO getSegmentFormData(IdentifierEntity ruleInfo) {
        TyplmSegmentCodeConditionDTO typlmSegmentCodeConditionDTO = new TyplmSegmentCodeConditionDTO();
        typlmSegmentCodeConditionDTO.setRuleInfo(ruleInfo);
        typlmSegmentCodeConditionDTO.setOtype(TableTypeConstrant.TY_DOC_OTYPE);
        return typlmSegmentCodeService.getSegmentFormData(typlmSegmentCodeConditionDTO);
    }

    private JSONObject buildOutRuleRemoteJSON(DocumentVO documentVO, String ruleId, String jobId, String projectId) {
        JSONObject jsonObject = new JSONObject();
        JSONArray jsonArray = new JSONArray();
        if (documentVO.getOid().contains("ty.inteplm.doc.CTyDoc:")) {
            documentVO.setDocOid(documentVO.getOid().replace("ty.inteplm.doc.CTyDoc:", ""));
        }
        jsonArray.add(documentVO);
        jsonObject.put("docList", jsonArray);
        jsonObject.put("docOtype", "ty.inteplm.doc.CTyDoc");
        jsonObject.put("parentNodeOtype", "ty.projectmng.job.CTyJob");
        jsonObject.put("parentNodeOid", jobId);
        jsonObject.put("projectOid", projectId);
        jsonObject.put("localRepositoryUid", "javaPDM");
        jsonObject.put("outputRuleOid", ruleId);
        jsonObject.put("type", "OUT");
        return jsonObject;
    }

    public JSONObject buildOAFormData(String formCode) {
        JSONObject data = new JSONObject();
        data.put("templeteNumber", formCode.replaceAll("\\r|\\n", ""));
        data.put("loginName", "22051030");
        return data;
    }

    @Override
    public void uploadFile(OaUploadFileDTO oaUploadFileDTO) {
        List<String> numberList = oaUploadFileDTO.getNumberList();
        List<String> typeList = oaUploadFileDTO.getTypeList();
        List<String> versionList = oaUploadFileDTO.getVersionList();

        // 参数校验
        validateUploadParams(numberList, typeList, versionList);

        // 批量处理文件上传
        batchProcessFileUpload(numberList, typeList, versionList);
    }

    /**
     * 验证上传参数
     */
    private void validateUploadParams(List<String> numberList, List<String> typeList, List<String> versionList) {
        if (CollUtil.isEmpty(numberList) || CollUtil.isEmpty(typeList) || CollUtil.isEmpty(versionList)) {
            throw RTErrorCodeEnum.NOT_FOUND_FILE_TEMPLATE.getException("参数列表不能为空");
        }

        if (numberList.size() != typeList.size() || numberList.size() != versionList.size()) {
            throw RTErrorCodeEnum.NOT_FOUND_FILE_TEMPLATE.getException("参数列表长度不一致");
        }
    }

    /**
     * 批量处理文件上传
     */
    private void batchProcessFileUpload(List<String> numberList, List<String> typeList, List<String> versionList) {
        for (int i = 0; i < numberList.size(); i++) {
            String number = numberList.get(i);
            String type = typeList.get(i);
            String version = versionList.get(i);
            if(StringUtils.isNotBlank(type) && type.toUpperCase().contains("CAD")){
                List<CADDocDO> cadDocList = typlmCADDocumentService.queryCADDocByNumberAndVersion(
                        number, version, "allVersion");
                processDocumentFiles(cadDocList, this::downloadCADFile);
            }else if(StringUtils.isNotBlank(type) && type.toUpperCase().contains("DOC")){
                List<DocDO> docList = typlmDocQueryService.queryDocByNumberAndVersion(
                        number, version, "allVersion");
                processDocumentFiles(docList, this::downloadDOCFile);
            }
        }
    }

    /**
     * 通用文档文件处理
     */
    private <T> void processDocumentFiles(List<T> documentList, DocumentProcessor<T> processor) {
        if (CollUtil.isEmpty(documentList)) {
            log.debug("未找到对应的文档记录");
            return;
        }
        for (T document : documentList) {
            processor.process(document);
        }
    }

    /**
     * 下载CAD文件
     */
    private void downloadCADFile(CADDocDO cadDocDO) {
        downloadFileByEntity(cadDocDO.getOid(), cadDocDO.getOtype());
    }

    /**
     * 下载DOC文件
     */
    private void downloadDOCFile(DocDO docDO) {
        downloadFileByEntity(docDO.getOid(), docDO.getOtype());
    }

    /**
     * 根据实体下载文件
     */
    private void downloadFileByEntity(BigInteger oid, String otype) {
        List<FileDataVO> fileList = typlmFileHandleService.getFileListByEntity(
                new IdentifierEntity(oid, otype));

        if (CollUtil.isEmpty(fileList)) {
            log.debug("文档[oid: {}, otype: {}]未找到关联文件", oid, otype);
            return;
        }

        for (FileDataVO fileDataVO : fileList) {
            downloadSingleFile(fileDataVO, oid);
        }
    }

    /**
     * 下载单个文件
     */
    private void downloadSingleFile(FileDataVO fileDataVO, BigInteger documentOid) {
        try {
            String filePath = FILE_PATH + documentOid + "/";
            commonService.downloadFileToFtp(new BigInteger(fileDataVO.getFileID()), filePath);
            log.debug("文件下载成功: 文件ID={}, 路径={}", fileDataVO.getFileID(), filePath);
        } catch (Exception e) {
            log.error("文件下载失败: 文件ID={}, 文档OID={}", fileDataVO.getFileID(), documentOid, e);
        }
    }


    @Override
    public List<DrawingInfoDTO> queryEBOMPartByNumber(PartNumberDTO partNumberDTO) {
        String partNumber = partNumberDTO.getPartNumber().trim();
        TyplmEntityBaseUtil.checkParamNotNull(partNumber, "编码");

        List<PartDO> partDOS = rtPartMapper.queryLatestPartByVersionAndView(partNumber, "Design", null, "EBOM_NUM");
        if (CollUtil.isEmpty(partDOS)) {
            return new ArrayList<>();
        }

        return processPartBomTreeData(partDOS, partNumber);
    }

    @Override
    public List<DrawingInfoDTO> queryPatternCodePartByNumber(PartNumberDTO partNumberDTO) {
        String partNumber = partNumberDTO.getPartNumber().trim();
        TyplmEntityBaseUtil.checkParamNotNull(partNumber, "编码");

        if (StringUtils.isBlank(partNumber) || partNumber.trim().length() < 3) {
            throw RTErrorCodeEnum.DRAWING_CODE_NAME_ERROR.getException("零件号长度必须大于等于3位");
        }

        List<PartDO> partDOS = rtPartMapper.queryLatestPartByVersionAndView("*" + partNumber + "*", "Design", null, "PatternCode");
        if (CollUtil.isEmpty(partDOS)) {
            return new ArrayList<>();
        }

        return processPartDOData(partDOS);
    }

    /**
     * 处理BOM树数据
     */
    private List<DrawingInfoDTO> processPartBomTreeData(List<PartDO> partDOS, String partNumber) {
        List<DrawingInfoDTO> result = new ArrayList<>();
        Map<BigInteger, StdTypeDetailDTO> typeData = getTypeData(
                Arrays.asList("ty.inteplm.cad.CTyCADDoc", "ty.inteplm.doc.CTyDoc"));

        for (PartDO partDO : partDOS) {
            PartBomTreeByFilterVO bomTreeVO = queryChildParts(
                    partNumber, partDO.getOid().toString(), "Design", true, null, "N", false);

            if (bomTreeVO == null || CollUtil.isEmpty(bomTreeVO.getPartBomTreeList())) {
                continue;
            }

            List<PartBomTree> partBomTreeList = bomTreeVO.getPartBomTreeList();
            List<BigInteger> partOidList = collectPartOids(partBomTreeList);

            // 批量查询相关数据
            DataBatchResult batchResult = batchQueryRelatedData(partOidList, typeData);

            // 处理BOM树
            for (PartBomTree partBomTree : partBomTreeList) {
                DrawingInfoDTO drawingInfoDTO = buildDrawingInfoDTO(partBomTree, "1", batchResult);
                result.add(drawingInfoDTO);
                buildLevelNumbers(partBomTree.getChildren(), "1", result, batchResult);
            }
        }
        return result;
    }

    /**
     * 处理PartDO数据
     */
    private List<DrawingInfoDTO> processPartDOData(List<PartDO> partDOS) {
        Map<BigInteger, StdTypeDetailDTO> typeData = getTypeData(
                Arrays.asList("ty.inteplm.cad.CTyCADDoc", "ty.inteplm.doc.CTyDoc"));

        List<BigInteger> partOidList = partDOS.stream()
                .map(PartDO::getOid)
                .collect(Collectors.toList());

        // 批量查询相关数据
        DataBatchResult batchResult = batchQueryRelatedData(partOidList, typeData);

        // 查询Part IBA属性
        Map<String, List<AttributeVO>> ibaValuePart = typlmAttributeValueService.getAttrValueTableByEntitys(
                partOidList, TableTypeConstrant.TY_PART_OTYPE);

        // 构建结果
        return partDOS.stream()
                .map(partDO -> buildDrawingInfoDTO(partDO,
                                                   ibaValuePart.get(partDO.getOid().toString()), batchResult))
                .collect(Collectors.toList());
    }

    /**
     * 批量查询相关数据
     */
    private DataBatchResult batchQueryRelatedData(List<BigInteger> partOidList,
                                                  Map<BigInteger, StdTypeDetailDTO> typeData) {
        DataBatchResult result = new DataBatchResult();
        result.setTypeData(typeData);

        if (CollUtil.isEmpty(partOidList)) {
            return result;
        }

        // 批量查询CAD文档
        result.setCadMap(typlmPartRelObjectService.batchQueryLinkCadMapByPartList(partOidList));

        // 批量查询描述文档
        List<PartDescribeDocVO> partDescribeDocVOS = typlmPartDescribeLinkService
                .batchQueryLatestDescribeDocByPartOids(partOidList, null);
        result.setDocMap(buildDocMap(partDescribeDocVOS));

        // 构建实体列表
        List<IdentifierEntity> entityList = buildEntityList(result.getCadMap(), partDescribeDocVOS);
        result.setIdentifierEntityListMap(typlmFileHandleService.batchGetFileListByEntity(entityList));

        // 批量获取IBA属性
        result.setCadIbaAttributesMap(batchGetCadIbaAttributes(result.getCadMap(), partDescribeDocVOS));

        return result;
    }

    /**
     * 构建文档映射
     */
    private Map<BigInteger, List<PartDescribeDocVO>> buildDocMap(List<PartDescribeDocVO> partDescribeDocVOS) {
        if (CollUtil.isEmpty(partDescribeDocVOS)) {
            return new HashMap<>();
        }

        return partDescribeDocVOS.stream()
                .filter(doc -> doc != null && doc.getPartOid() != null)
                .collect(Collectors.groupingBy(
                        PartDescribeDocVO::getPartOid,
                        Collectors.mapping(doc -> doc, Collectors.toList())
                ));
    }

    /**
     * 构建实体列表
     */
    private List<IdentifierEntity> buildEntityList(Map<BigInteger, List<CADDocDO>> cadMap,
                                                   List<PartDescribeDocVO> partDescribeDocVOS) {
        List<IdentifierEntity> entityList = new ArrayList<>();

        // 添加文档实体
        if (CollUtil.isNotEmpty(partDescribeDocVOS)) {
            partDescribeDocVOS.stream()
                    .filter(doc -> doc != null && doc.getOid() != null)
                    .forEach(doc -> entityList.add(new IdentifierEntity(doc.getOid(), doc.getOtype())));
        }

        // 添加CAD实体
        if (CollUtil.isNotEmpty(cadMap)) {
            cadMap.values().stream()
                    .flatMap(List::stream)
                    .filter(cadDoc -> cadDoc != null && cadDoc.getOid() != null)
                    .forEach(cadDoc -> entityList.add(new IdentifierEntity(cadDoc.getOid(), cadDoc.getOtype())));
        }

        return entityList;
    }

    public Map<BigInteger, StdTypeDetailDTO> getTypeData(List<String> typeNames) {
        List<TypeVueVO> typeVueVOS = typlmTypeMapper.queryAccessTypeByNames(typeNames);

        if (CollUtil.isEmpty(typeVueVOS)) {
            return new HashMap<>();
        }

        List<BigInteger> typeOids = typeVueVOS.stream()
                .map(TypeVueVO::getOid)
                .collect(Collectors.toList());

        List<BigInteger> childrenList = typlmTypeMapper.batchQueryAllChildrenIdByOid(typeOids, false);

        if (CollUtil.isEmpty(childrenList)) {
            return new HashMap<>();
        }

        List<StdTypeDetailDTO> stdTypeDetailDTOS = stdTypeMapper.queryTypeDetailsByOid(childrenList);

        return stdTypeDetailDTOS.stream()
                .collect(Collectors.toMap(
                        StdTypeDetailDTO::getTypeOid,
                        dto -> dto,
                        (e1, e2) -> e1
                ));
    }

    /**
     * 收集部件OID列表
     */
    private List<BigInteger> collectPartOids(List<PartBomTree> partBomTreeList) {
        if (CollUtil.isEmpty(partBomTreeList)) {
            return new ArrayList<>();
        }

        return partBomTreeList.stream()
                .map(partBomTree -> new BigInteger(partBomTree.getPartoid()))
                .collect(Collectors.toList());
    }

    public PartBomTreeByFilterVO queryChildParts(String partNumber, String partOid, String view,
                                                 boolean isNestedTree, String lifecycleStateKey,
                                                 String expandLevel, boolean isParent) {
        ChildPartsFiltersDTO childPartsFiltersDTO = new ChildPartsFiltersDTO();
        childPartsFiltersDTO.setExpandCondition("latest");
        childPartsFiltersDTO.setView(view);

        if (StringUtils.isNotBlank(lifecycleStateKey)) {
            childPartsFiltersDTO.setLifecycleStateKey(lifecycleStateKey);
        }

        ExpandBomTreeByFilterDTO expandBomTreeByFilterDTO = new ExpandBomTreeByFilterDTO();
        expandBomTreeByFilterDTO.setConditions(Collections.singletonList(childPartsFiltersDTO));
        expandBomTreeByFilterDTO.setFatherOid(partOid);
        expandBomTreeByFilterDTO.setFatherNumber(partNumber);
        expandBomTreeByFilterDTO.setFatherOtype(TableTypeConstrant.TY_PART_OTYPE);
        expandBomTreeByFilterDTO.setExpandLevel(expandLevel);
        expandBomTreeByFilterDTO.setIbaColumns(null);
        expandBomTreeByFilterDTO.setIsQueryIBA(true);
        expandBomTreeByFilterDTO.setIsNestedTree(isNestedTree);

        return typlmPartBomService.expandBomTreeByFilter(expandBomTreeByFilterDTO, isParent);
    }

    /**
     * 解析Part IBA属性Map
     */
    public Map<String, List<String>> parsePartIbaAttributeMap(Map<String, List<String>> partIbaAttributeMap) {
        if (partIbaAttributeMap == null) {
            return new HashMap<>();
        }

        return partIbaAttributeMap.entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> {
                            String originalKey = entry.getKey();
                            int firstCommaIndex = originalKey.indexOf(",");
                            if (firstCommaIndex > 0 && firstCommaIndex < originalKey.length() - 1) {
                                return originalKey.substring(firstCommaIndex + 1).trim();
                            }
                            return originalKey;
                        },
                        Map.Entry::getValue,
                        (e1, e2) -> e1
                ));
    }

    /**
     * 批量获取CAD文档的IBA属性
     */
    private Map<String, Map<String, String>> batchGetCadIbaAttributes(Map<BigInteger, List<CADDocDO>> cadMap,
                                                                      List<PartDescribeDocVO> partDescribeDocVOS) {
        if (CollUtil.isEmpty(cadMap) && CollUtil.isEmpty(partDescribeDocVOS)) {
            return new HashMap<>();
        }

        Set<BigInteger> docOidSet = new HashSet<>();

        // 收集CAD文档OID
        if (CollUtil.isNotEmpty(cadMap)) {
            cadMap.values().stream()
                    .flatMap(List::stream)
                    .filter(cadDoc -> cadDoc != null && cadDoc.getOid() != null)
                    .forEach(cadDoc -> docOidSet.add(cadDoc.getOid()));
        }

        // 收集描述文档OID
        if (CollUtil.isNotEmpty(partDescribeDocVOS)) {
            partDescribeDocVOS.stream()
                    .filter(doc -> doc != null && doc.getOid() != null)
                    .forEach(doc -> docOidSet.add(doc.getOid()));
        }

        if (CollUtil.isEmpty(docOidSet)) {
            return new HashMap<>();
        }

        List<BigInteger> docOidList = new ArrayList<>(docOidSet);
        Map<String, List<AttributeVO>> cadIba = typlmAttributeValueService.getAttrValueTableByEntitys(
                docOidList, TableTypeConstrant.TY_CADDOC_OTYPE);

        return cadIba.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> convertAttributeVOsToMap(entry.getValue()),
                        (e1, e2) -> e1
                ));
    }

    /**
     * 构建DrawingInfoDTO (用于PartBomTree)
     */
    private DrawingInfoDTO buildDrawingInfoDTO(PartBomTree partBomTree, String level, DataBatchResult batchResult) {
        DrawingInfoDTO dto = new DrawingInfoDTO();
        dto.setLevel(level);
        dto.setPartNumber(partBomTree.getObjectNumber());
        dto.setPartName(partBomTree.getName());

        // 处理Part IBA属性
        Map<String, List<String>> partIbaMap = parsePartIbaAttributeMap(partBomTree.getPartIbaAttributeMap());
        Map<String, List<String>> ibaMap = parsePartIbaAttributeMap(partBomTree.getIbaAttributeMap());

        dto.setDrawingDescription(getFirstElementOrEmpty(partIbaMap.get("CompleteDescription")));
        dto.setManufacturingAttribute(getFirstElementOrEmpty(ibaMap.get("ManufacturingAttr")));

        // 处理CAD文档信息
        BigInteger partOid = new BigInteger(partBomTree.getPartoid());
        processCadInfo(dto, batchResult.getCadMap().get(partOid), batchResult);

        return dto;
    }


    /**
     * 构建DrawingInfoDTO (用于PartDO)
     */
    private DrawingInfoDTO buildDrawingInfoDTO(PartDO partDO, List<AttributeVO> attributeVOS, DataBatchResult batchResult) {
        DrawingInfoDTO dto = new DrawingInfoDTO();
        dto.setPartNumber(partDO.getObjnumber());
        dto.setPartName(partDO.getName());

        // 处理IBA属性
        Map<String, String> ibaMap = convertAttributeVOsToMap(attributeVOS);
        dto.setDrawingDescription(ibaMap.getOrDefault("CompleteDescription", ""));
        dto.setReferenceDrawingNumber(ibaMap.getOrDefault("ReferencePartDrawingNumber", ""));
        dto.setChangePoint(ibaMap.getOrDefault("ChangePoint", ""));

        // 处理CAD文档信息
        processCadInfo(dto, batchResult.getCadMap().get(partDO.getOid()), batchResult);

        return dto;
    }

    /**
     * 处理CAD文档信息
     */
    private void processCadInfo(DrawingInfoDTO dto, List<CADDocDO> cadDocList, DataBatchResult batchResult) {
        if (CollUtil.isEmpty(cadDocList)) {
            return;
        }

        CADDocDO cadDocDO = cadDocList.get(0);
        List<FileDataVO> fileDataVOS = batchResult.getIdentifierEntityListMap().get(
                new IdentifierEntity(cadDocDO.getOid(), cadDocDO.getOtype()));

        if (CollUtil.isNotEmpty(fileDataVOS)) {
            FileDataVO fileDataVO = fileDataVOS.get(0);
            dto.setDrawing(FILE_PATH + cadDocDO.getOid() + "/" + fileDataVO.getFileName());
        }

        dto.setSystemVersion(cadDocDO.getVersion() + "." + cadDocDO.getIterationid());

        // 设置绘图类型
        StdTypeDetailDTO typeDetail = batchResult.getTypeData().get(cadDocDO.getTypeoid());
        if (typeDetail != null) {
            String inthid = typeDetail.getInthid();
            if (inthid.contains("二维图纸")) {
                dto.setDrawingType("CAD|AutoCAD");
            } else if (inthid.contains("三维数模")) {
                dto.setDrawingType("CAD|UG");
            }
        }

        // 设置业务版本
        Map<String, String> cadIbaMap = batchResult.getCadIbaAttributesMap().get(cadDocDO.getOid().toString());
        if (cadIbaMap != null) {
            dto.setBusinessVersion(cadIbaMap.get("BusinessEdition"));
        }
    }

    /**
     * 递归构建层级编号
     */
    private void buildLevelNumbers(Set<PartBomTree> children, String parentNumber,
                                   List<DrawingInfoDTO> result, DataBatchResult batchResult) {
        if (CollUtil.isEmpty(children)) {
            return;
        }

        int index = 1;
        for (PartBomTree child : children) {
            String currentNumber = parentNumber.isEmpty() ? String.valueOf(index) : parentNumber + "." + index;

            DrawingInfoDTO dto = buildDrawingInfoDTO(child, currentNumber, batchResult);
            result.add(dto);

            buildLevelNumbers(child.getChildren(), currentNumber, result, batchResult);
            index++;
        }
    }

    /**
     * 获取List中的第一个元素，如果List为空或为null则返回空字符串
     */
    private String getFirstElementOrEmpty(List<String> list) {
        return list != null && !list.isEmpty() ? list.get(0) : "";
    }

    /**
     * 将AttributeVO列表转换为Map
     */
    private Map<String, String> convertAttributeVOsToMap(List<AttributeVO> attributeVOS) {
        if (CollUtil.isEmpty(attributeVOS)) {
            return new HashMap<>();
        }

        return attributeVOS.stream()
                .filter(attr -> attr != null && attr.getAttributeEnName() != null && attr.getAttributeValue() != null)
                .collect(Collectors.toMap(
                        AttributeVO::getAttributeEnName,
                        AttributeVO::getAttributeValue,
                        (e1, e2) -> e1
                ));
    }

    /**
     * 批量查询结果封装类
     */
    @Data
    private static class DataBatchResult {
        private Map<BigInteger, List<CADDocDO>> cadMap;
        private Map<BigInteger, List<PartDescribeDocVO>> docMap;
        private Map<IdentifierEntity, List<FileDataVO>> identifierEntityListMap;
        private Map<String, Map<String, String>> cadIbaAttributesMap;
        private Map<BigInteger, StdTypeDetailDTO> typeData;
    }
}


